package com.zdj.bean_validation;

import org.hibernate.validator.internal.engine.DefaultClockProvider;
import org.hibernate.validator.internal.engine.DefaultParameterNameProvider;
import org.hibernate.validator.internal.engine.ValidatorContextImpl;
import org.hibernate.validator.internal.engine.ValidatorFactoryImpl;
import org.junit.Test;

import javax.el.ExpressionFactory;
import javax.validation.*;
import javax.validation.metadata.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Set;

/**
 * @author zdj
 * @date 2023-03-21 17:07
 */
public class BeanValidationTest {



    @Test
    public void t1() {
        Person person = new Person();
        person.setAge(-1);

        // 1、使用【默认配置】得到一个校验工厂  这个配置可以来自于provider、SPI提供
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        // 2、得到一个校验器
        Validator validator = validatorFactory.getValidator();
        // 3、校验Java Bean（解析注解） 返回校验结果
        Set<ConstraintViolation<Person>> result = validator.validate(person);

        // 输出校验结果
        result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue()).forEach(System.out::println);
    }

    @Test
    public void t2() {
        ExpressionFactory expressionFactory = ExpressionFactory.newInstance();
        System.out.println(expressionFactory);
    }

    @Test
    public void t3() {
        for (Field declaredField : Person.class.getDeclaredFields()) {
            Annotation[] annotations1 = declaredField.getAnnotations();
            System.out.println(annotations1);
        }
    }

    @Test
    public void t4() throws NoSuchMethodException {
        new PersonService().getOne(0, "A哥");
    }

    @Test
    public void t5() throws NoSuchMethodException {
        // 看到没 IDEA自动帮你前面加了个notNull
        Person result = new PersonService().getOneValidReturn(1, "A哥");
    }

    @Test
    public void t6() throws NoSuchMethodException {
        // save.arg0 不能为null: null
//         new PersonService().save(null);
        new PersonService().save(new Person());
    }

    /**
     * 校验Java Bean所有约束中的所有包括：1、属性上的约束 2、「类上的约束」
     */
    @Test
    public void t7() {
        User user = new User();
        user.setName("YourBatman");

        Set<ConstraintViolation<User>> result = ValidatorUtil.obtainValidator().validate(user);
        ValidatorUtil.printViolations(result);
    }

    /**
     * 它会校验属性上的「所有」约束，注意只是属性上的哦，其它地方的不管。
     */
    @Test
    public void t8() {
        User user = new User();
        user.setFullName("YourBatman");

        Set<ConstraintViolation<User>> result = ValidatorUtil.obtainValidator().validateProperty(user, "fullName");
        ValidatorUtil.printViolations(result);
    }

    /**
     * 直接校验某个值是否满足某个属性的所有约束，所以它可以做事前校验判断
     */
    @Test
    public void t9() {
        Set<ConstraintViolation<User>> result = ValidatorUtil.obtainValidator().validateValue(User.class, "fullName", "A哥");
        ValidatorUtil.printViolations(result);
    }

    /**
     * 描述受约束的Java Bean和与其关联的约束
     */
    @Test
    public void t10() {
        BeanDescriptor beanDescriptor = ValidatorUtil.obtainValidator().getConstraintsForClass(User.class);
        System.out.println("此类是否需要校验：" + beanDescriptor.isBeanConstrained());

        // 获取属性、方法、构造器的约束
        Set<PropertyDescriptor> constrainedProperties = beanDescriptor.getConstrainedProperties();
        Set<MethodDescriptor> constrainedMethods = beanDescriptor.getConstrainedMethods(MethodType.GETTER);
        Set<ConstructorDescriptor> constrainedConstructors = beanDescriptor.getConstrainedConstructors();
        System.out.println("需要校验的属性：" + constrainedProperties);
        System.out.println("需要校验的方法：" + constrainedMethods);
        System.out.println("需要校验的构造器：" + constrainedConstructors);

        PropertyDescriptor fullNameDesc = beanDescriptor.getConstraintsForProperty("fullName");
        System.out.println(fullNameDesc);
        System.out.println("fullName属性的约束注解个数：" + fullNameDesc.getConstraintDescriptors().size());
    }

    /**
     * 校验器上下文，根据此上下文创建Validator实例。不同的上下文可以创建出不同实例（这里的不同指的是内部组件不同），满足各种个性化的定制需求。
     */
    @Test
    public void t11() {
        ValidatorFactoryImpl validatorFactory = (ValidatorFactoryImpl) ValidatorUtil.obtainValidatorFactory();
        // 使用默认的Context上下文，并且初始化一个Validator实例
        // 必须传入一个校验器工厂实例哦
        ValidatorContext validatorContext = new ValidatorContextImpl(validatorFactory)
                .parameterNameProvider(new DefaultParameterNameProvider())
                .clockProvider(DefaultClockProvider.INSTANCE);

        // 通过该上下文，生成校验器实例（注意：调用多次，生成实例是多个哟）
        System.out.println(validatorContext.getValidator());
    }

    /**
     * 该方法用于得到一个ValidatorContext实例，它具有高度抽象、与底层API无关的特点，「是推荐的获取方式」，并且使用起来有流式编程的效果
     */
    @Test
    public void t12() {
        ValidatorContext validatorContext = ValidatorUtil.obtainValidatorFactory().usingContext()
                .parameterNameProvider(new DefaultParameterNameProvider())
                .clockProvider(DefaultClockProvider.INSTANCE);
        System.out.println(validatorContext);
    }

    /**
     * 工厂直接获取Validator
     */
    @Test
    public void t13() {
        Validator validator = ValidatorUtil.obtainValidatorFactory().getValidator();
    }

    /**
     * 从上下文ValidatorContext获取Validator
     */
    @Test
    public void t14() {
        Validator validator = ValidatorUtil.obtainValidatorFactory().usingContext()
                .parameterNameProvider(new DefaultParameterNameProvider())
                .clockProvider(DefaultClockProvider.INSTANCE)
                .getValidator();
    }
}
